package main

import (
	"fmt"
	"github.com/go-redis/redis/v7"
	jsoniter "github.com/json-iterator/go"
	"strconv"
)

// 声明一个全局的rdb变量
var rdb *redis.Client

// 初始化连接
func initClient() (err error) {
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password set
		DB:       0,  // use default DB
	})

	_, err = rdb.Ping().Result()
	if err != nil {
		return err
	}
	return nil
}

type Department struct {
	Id int
	Name string
	AccountList []*Account
}

type Account struct {
	Id int
	Name string
}

func redisExample() {

	var dList []*Department
	for i:=1; i<=10;i++{
		var d = Department{
			Id:          i,
			Name:        strconv.Itoa(i) + "部门",
		}
		for j:=1; j<=10;j++{
			var a = Account{
				Id:   j,
				Name: strconv.Itoa(j) + "用户",
			}
			d.AccountList = append(d.AccountList, &a)
		}
		dList = append(dList, &d)
	}

	departmentListStr, _ := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(&dList)

	ierr := rdb.Set("3243534634646634", string(departmentListStr), 0).Err()
	if ierr != nil {
		fmt.Printf("set departmentList failed, err:%v\n", ierr)
		return
	}

	departmentList, qerr := rdb.Get("3243534634646634").Result()
	if qerr != nil {
		fmt.Printf("get departmentList failed, err:%v\n", qerr)
		return
	}
	fmt.Println("departmentList：", departmentList)

	err := rdb.Set("score", 100, 0).Err()
	if err != nil {
		fmt.Printf("set score failed, err:%v\n", err)
		return
	}

	val, err := rdb.Get("score").Result()
	if err != nil {
		fmt.Printf("get score failed, err:%v\n", err)
		return
	}
	fmt.Println("score", val)

	val1, err := rdb.Del("score").Result()
	if err != nil {
		fmt.Printf("get score failed, err:%v\n", err)
		return
	}
	fmt.Println("score1", val1)

	val2, err := rdb.Get("score").Result()
	if err == redis.Nil {
		fmt.Println("score does not exist")
	} else if err != nil {
		fmt.Printf("get name failed, err:%v\n", err)
		return
	} else {
		fmt.Println("name2", val2)
	}

	rdb.FlushDB()
	// 将一条数据添加到列表的头部（类似入栈）
	rdb.LPush("mylist","val1")
	// 将一条或多条数据添加到列表的尾部
	rdb.RPush("mylist","val99","val100")
	s1:=[]string{"val2","val3","val4"}
	// 将一个切片添加到列表的头部
	rdb.LPush("mylist",s1)

	// 获取List的长度
	fmt.Println(rdb.LLen("mylist").Val())	//6

	// 获取List中的元素：起始索引~结束索引，当结束索引 > llen(list)或=-1时，取出全部数据
	// 遍历List，获取每一个元素
	// 注意取出来的顺序！！！
	// 注意取出来的顺序！！！
	// 注意取出来的顺序！！！
	s2:=rdb.LRange("mylist",0,-1).Val()
	for _, i := range s2 {
		fmt.Println(i)	// [val5 val4 val3 val2 val1 val99 val100]
	}
	fmt.Println(rdb.LRange("mylist",0,3).Val())	// [val4 val3 val2 val1]
	fmt.Println(rdb.LRange("mylist",0,100).Val())// [val4 val3 val2 val1 val99 val100]

	// 移除List的第一个元素（头元素）
	rdb.LPop("mylist")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())// [val3 val2 val1 val99 val100]

	// 移除List的最后一个元素（尾元素）
	rdb.RPop("mylist")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())// [val3 val2 val1 val99]

	// 移除List 指定个数的指定值
	rdb.LPush("mylist","val3","val3","val3","val2")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())
	fmt.Println(rdb.LLen("mylist"))	//llen mylist: 8
	// 此时的mylist： [val2 val3 val3 val3 val3 val2 val1 val99]
	//移除list中的1个val3
	rdb.LRem("mylist",1,"val3")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())// [val2 val3 val3 val3 val2 val1 val99]
	// 移除剩下的3个值：val3(当移除的个数count大于该值的实际个数时，全部移除)
	rdb.LRem("mylist",100,"val3")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())// [val2 val2 val1 val99]

	//截取List(通过下标，前闭后闭,会修改原数据)
	rdb.LTrim("mylist",1,2)
	fmt.Println(rdb.LRange("mylist",0,-1).Val())	// [val2 val1]
	// 截取的结束下标大于List长度或者-1时,一直截取到末尾
	rdb.LTrim("mylist",1,100)
	fmt.Println(rdb.LRange("mylist",0,-1).Val())	// [val1]

	rdb.RPush("mylist","val2","val3")
	//检查数据库是否存在此List,1:存在 0：不存在
	fmt.Println(rdb.Exists("mylist2").Val())	// 0
	//移除List的最后一个元素,并移动到另一个列表(若没有此列表则创建)
	rdb.RPopLPush("mylist","mylist2")
	fmt.Printf("mylist=%v\n",rdb.LRange("mylist",0,-1).Val())	// mylist=[val1 val2]
	fmt.Printf("mylist2=%v\n",rdb.LRange("mylist2",0,-1).Val())	// mylist2=[val3]

	// 通过下标更新List的值
	rdb.LSet("mylist",1,"val200")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())	//[val1 val200]
	// 更新的下标>List的长度会触发panic
	//err:=cli.LSet("mylist",2,"val3").Err()
	//if err != nil {
	//	panic(err)	// panic: ERR index out of range
	//}
	//	当前List不存在,会触发panic
	//err:=cli.LSet("mylist3",1,"qqqqq").Err()
	//if err != nil {
	//	panic(err)	// panic: ERR no such key
	//}

	// 在列表指定的值之前或者之后插入值
	rdb.LInsertBefore("mylist","val1","qqq")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())	// [qqq val1 val200]
	rdb.LInsertAfter("mylist","qqq","hello~")
	fmt.Println(rdb.LRange("mylist",0,-1).Val())	// [qqq hello~ val1 val200]
	// 当标志位不存在时,插入值失败
	rdb.LInsertAfter("mylist","GOGO","golang").Err()
	fmt.Println(rdb.LRange("mylist",0,-1).Val())	// [qqq hello~ val1 val200]

	// 当删除链表内的所有值时，空链表也就不存在了
	for rdb.LLen("mylist").Val()>0{
		rdb.LPop("mylist")
		fmt.Println(rdb.LRange("mylist",0,-1).Val())
	}

	// 查看当前数据库所有key
	fmt.Println(rdb.Keys("*").Val())	// [mylist2]
}

func redisExample2() {
	zsetKey := "language_rank"
	languages := []*redis.Z{
		&redis.Z{Score: 90.0, Member: "Golang"},
		&redis.Z{Score: 98.0, Member: "Java"},
		&redis.Z{Score: 95.0, Member: "Python"},
		&redis.Z{Score: 97.0, Member: "JavaScript"},
		&redis.Z{Score: 99.0, Member: "C/C++"},
	}
	// ZADD
	num, err := rdb.ZAdd(zsetKey, languages...).Result()
	if err != nil {
		fmt.Printf("zadd failed, err:%v\n", err)
		return
	}
	fmt.Printf("zadd %d succ.\n", num)

	// 把Golang的分数加10
	newScore, err := rdb.ZIncrBy(zsetKey, 10.0, "Golang").Result()
	if err != nil {
		fmt.Printf("zincrby failed, err:%v\n", err)
		return
	}
	fmt.Printf("Golang's score is %f now.\n", newScore)

	// 取分数最高的3个
	ret, err := rdb.ZRevRangeWithScores(zsetKey, 0, 2).Result()
	if err != nil {
		fmt.Printf("zrevrange failed, err:%v\n", err)
		return
	}
	for _, z := range ret {
		fmt.Println(z.Member, z.Score)
	}

	// 取95~100分的
	op := &redis.ZRangeBy{
		Min: "95",
		Max: "100",
	}
	ret, err = rdb.ZRangeByScoreWithScores(zsetKey, op).Result()
	if err != nil {
		fmt.Printf("zrangebyscore failed, err:%v\n", err)
		return
	}
	for _, z := range ret {
		fmt.Println(z.Member, z.Score)
	}
}

func main()  {
	initClient()
	redisExample()
	redisExample2()
}